using Fantasy.SourceGenerator.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Fantasy.SourceGenerator.Generators
{
    [Generator]
    public sealed class PoolCreatorGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            var poolTypes = context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => PotentialSyntax(node),
                    transform: static (ctx, _) => GetPoolTypeInfo(ctx))
                .Where(static info => info != null)
                .Collect()
                .Select(static (types, _) => types.Distinct().ToList());
            
            var compilationAndTypes = context.CompilationProvider.Combine(poolTypes);
            
            context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
            {
                if (CompilationHelper.IsSourceGeneratorDisabled(source.Left))
                {
                    return;
                }

                if (!CompilationHelper.HasFantasyDefine(source.Left))
                {
                    return;
                }

                if (source.Left.GetTypeByMetadataName("Fantasy.Pool.IPool") == null)
                {
                    return;
                }

                GeneratePoolCreators(spc, source.Left, source.Right!);
            });
        }

        private static bool PotentialSyntax(SyntaxNode node)
        {
            // 拿到非抽象类定义和闭合泛型使用
            return (node is ClassDeclarationSyntax classDecl &&
                !classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword))) || 
                node is GenericNameSyntax;
        }

        private static string? GetPoolTypeInfo(GeneratorSyntaxContext context)
        {
            var symbol = context.Node switch
            {
                ClassDeclarationSyntax classDecl => context.SemanticModel.GetDeclaredSymbol(classDecl),
                GenericNameSyntax genericName => context.SemanticModel.GetTypeInfo(genericName).Type as INamedTypeSymbol,
                _ => null
            };

            if (symbol is null || !IsValidPoolType(symbol))
            {
                return null;
            }

            return symbol.GetFullName();            
        }

        private static bool IsValidPoolType(INamedTypeSymbol s)
        {
            if (s.DeclaredAccessibility != Accessibility.Public || s.IsAbstract)
            {
                return false;
            }

            if (s.IsOpenGeneric())
            {
                return false;
            }

            var implementsIPool = s.AllInterfaces.Any(i =>
                i.Name == "IPool" &&
                i.ContainingNamespace.ToString() == "Fantasy.Pool");

            if (!implementsIPool)
            {
                return false;
            }

            return s.Constructors.Any(c =>
                c.Parameters.Length == 0 &&
                (c.DeclaredAccessibility == Accessibility.Public || c.DeclaredAccessibility == Accessibility.Internal));
        }

        private static void GeneratePoolCreators(
            SourceProductionContext context,
            Compilation compilation,
            IEnumerable<string> poolTypes)
        {
            var validTypes = poolTypes.ToList();
            var builder = new SourceCodeBuilder();
            var markerClassName = compilation.GetAssemblyName("PoolCreatorGeneratorRegistrar", out var assemblyName, out _);
            
            builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
            
            builder.AddUsings(
                "System",
                "System.Collections.Generic",
                "Fantasy.Pool",
                "Fantasy.Network.Interface",
                "Fantasy.Serialize"
            );
            builder.AppendLine();
             builder.BeginDefaultNamespace();
            builder.AddXmlComment($"Auto-generated PoolCreatorGenerator class for {assemblyName}");
            builder.BeginClass(markerClassName, "internal sealed","global::Fantasy.Assembly.IPoolCreatorGenerator");
            // 开始定义RuntimeTypeHandles方法
            builder.AddXmlComment("TypeHashCodes");
            builder.BeginMethod("public global::System.RuntimeTypeHandle[] RuntimeTypeHandles()");
            
            if (validTypes.Any())
            {
                builder.AppendLine($"var runtimeTypeHandles = new global::System.RuntimeTypeHandle[{validTypes.Count}];");
                for (var i = 0; i < validTypes.Count; i++)
                {
                    builder.AppendLine($"runtimeTypeHandles[{i}] = typeof({validTypes[i]}).TypeHandle;");
                }
                builder.AppendLine("return runtimeTypeHandles;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<global::System.RuntimeTypeHandle>();");
            }
            
            builder.EndMethod();
            // 开始定义TypeHashCodes方法
            builder.AddXmlComment("Generators");
            builder.BeginMethod("public Func<global::Fantasy.Pool.IPool>[] Generators()");
            
            if (validTypes.Any())
            {
                builder.AppendLine($"var generators = new Func<global::Fantasy.Pool.IPool>[{validTypes.Count}];");
                for (var i = 0; i < validTypes.Count; i++)
                {
                    builder.AppendLine($"generators[{i}] = () => new {validTypes[i]}();");
                }
                builder.AppendLine("return generators;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<Func<global::Fantasy.Pool.IPool>>();");
            }
            
            builder.EndMethod();
            builder.EndClass();
            builder.EndNamespace();
            context.AddSource($"{markerClassName}.g.cs", builder.ToString());
        }
    }
}
